using DataAnalyticsTools.Core;
using DataAnalyticsTools.Models;
using GrapeCity.Forguncy.Commands;
using GrapeCity.Forguncy.Log;
using GrapeCity.Forguncy.Plugin;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;

namespace DataAnalyticsTools
{
    [Icon("pack://application:,,,/DataAnalyticsTools;component/Resources/Icon.png")]
    [Designer("DataAnalyticsTools.Designer.DataAnalyticsToolsServerCommandDesigner, DataAnalyticsTools")]
    [Category("数据分析")]
    [OrderWeight(10)]
    public class TrendByChangePointsDetection : Command, ICommandExecutableInServerSideAsync, IServerCommandParamGenerator
    {
        [FormulaProperty]
        [DisplayName("时间序列数据（单变量）")]
        [Description("float[]数组，每个样本的值。")]
        public object featuresExpr { get; set; }

        [DisplayName("变化点检测方法")]
        public ChangePointDetectMethod Method { get; set; }

        [FormulaProperty]
        [DisplayName("敏感性")]
        [Description("检测变化点时采用的敏感度，根据算法不同，敏感度的含义也存在差异。")]
        public object sensitivityExpr { get; set; }

        [FormulaProperty]
        [DisplayName("窗口尺寸")]
        [Description("CUSUM: 影响局部基准计算，推荐 Max(3, dataLength/5)； SlidingWindow: 定义对比窗口大小，推荐 Max(3, dataLength/10)； Volatility: 定义波动率统计窗口，推荐 Max(5, dataLength/10)")]
        public object windowSizeExpr { get; set; }

        [ResultToProperty]
        [DisplayName("保存整体趋势到变量")]
        [Description("斜率在Slope属性，趋势强度在TrendStrength属性，趋势显著度在Significance属性。")]
        public string ResultTo { get; set; } = "总体趋势";

        [ResultToProperty]
        [DisplayName("保存变化点到变量")]
        [Description("变化点的索引在Index属性，评分在Score属性，变化类型在ChangeType属性，置信度在Confidence属性。")]
        public string ResultTo2 { get; set; } = "变化点数组";

        [ResultToProperty]
        [DisplayName("保存分段趋势到变量")]
        [Description("分段起始点索引在StartIndex属性，结束点索引在EndIndex属性，斜率在Slope属性，方向在Direction属性，趋势强度在TrendStrength属性。")]
        public string ResultTo3 { get; set; } = "分段趋势数组";


        public async Task<ExecuteResult> ExecuteAsync(IServerCommandExecuteContext dataContext)
        {
            var features = GeneralConvertors.ConvertToFloatArray(await dataContext.EvaluateFormulaAsync(featuresExpr));
            var sensitivity = float.Parse((await dataContext.EvaluateFormulaAsync(sensitivityExpr))?.ToString() ?? "1");
            var windowSize = int.Parse((await dataContext.EvaluateFormulaAsync(windowSizeExpr))?.ToString() ?? "5");

            // 根据不同的方法设置对应的敏感度参数
            var rawResults = Method switch
            {
                ChangePointDetectMethod.CUSUM => TrendAnalyzer.CalculateLinearTrendWithChangePoints(
                    features, Method, windowSize, cusumThresholdFactor: sensitivity),
                ChangePointDetectMethod.SlidingWindow => TrendAnalyzer.CalculateLinearTrendWithChangePoints(
                    features, Method, windowSize, slidingWindowAdjustmentFactor: sensitivity),
                ChangePointDetectMethod.Regression => TrendAnalyzer.CalculateLinearTrendWithChangePoints(
                    features, Method, windowSize, regressionSensitivityFactor: sensitivity),
                ChangePointDetectMethod.Volatility => TrendAnalyzer.CalculateLinearTrendWithChangePoints(
                    features, Method, windowSize, volatilitySensitivityFactor: sensitivity),
                _ => TrendAnalyzer.CalculateLinearTrendWithChangePoints(features, Method, windowSize)
            };
            dataContext.Parameters[ResultTo] = rawResults;
            dataContext.Parameters[ResultTo2] = rawResults.ChangePoints;
            dataContext.Parameters[ResultTo3] = rawResults.SegmentTrends;

            return new ExecuteResult();
        }

        public override string ToString()
        {
            if (featuresExpr == null)
            {
                return "计算特征趋势";
            }
            else
            {
                return "计算特征趋势（" + Method.ToString() + "）：" + ResultTo;
            }
        }

        public override CommandScope GetCommandScope()
        {
            return CommandScope.ExecutableInServer;
        }

        public IEnumerable<GenerateParam> GetGenerateParams()
        {

            List<GenerateParam> result = [];

            result.Add(new GenerateObjectParam()
            {
                ParamName = this.ResultTo,
                Description = "总体趋势",
                ParamScope = CommandScope.All,
                SubPropertiesDescription = new Dictionary<string, string>() {
                    { "Slope","斜率"},
                    { "TrendStrength","趋势强度" },
                    { "Significance","趋势显著性"},
                    { "Direction","趋势方向"},
                    { "FittedValues","拟合的线性值"},
                    { "Residuals","残差"},
                    { "Method","检测方法"}
                }
            });

            result.Add(new GenerateListParam()
            {
                ParamName = this.ResultTo2,
                Description = "变化点数组",
                ParamScope = CommandScope.All,
                ItemProperties = new List<string>() {
                    "Index",
                    "Score",
                    "ChangeType",
                    "Magnitude",
                    "Confidence",
                    "ConfidenceLabel"
                }
            });

            result.Add(new GenerateListParam()
            {
                ParamName = this.ResultTo3,
                Description = "分段趋势数组",
                ParamScope = CommandScope.All,
                ItemProperties = new List<string>() {
                    "StartIndex",
                    "EndIndex",
                    "Slope",
                    "TrendStrength",
                    "Direction"
                }
            });

            return result;
        }

    }

}
